package docview;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Vector;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.event.TableColumnModelListener;
import javax.swing.event.TableModelEvent;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
import javax.swing.DropMode;
import javax.swing.TransferHandler;
import tableprocessor.Table;


/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author Dema
 */

import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceContext;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import javax.swing.JCheckBox;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JToolBar;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableColumnModel;
import tableprocessor.ColumnPare;


class DocTableModel extends DefaultTableModel {
    public Vector getColumnIdentifiers() {
        return columnIdentifiers;
    }
}






class StringTransferable implements Transferable, ClipboardOwner {
  public static final DataFlavor plainTextFlavor = DataFlavor.plainTextFlavor;
  public static final DataFlavor localStringFlavor = DataFlavor.stringFlavor;
  private String string;
  public static final DataFlavor[] flavors = {
    StringTransferable.plainTextFlavor,
    StringTransferable.localStringFlavor
  };

  private static final List flavorList = Arrays.asList( flavors );

  public StringTransferable(String s)
  {
      string = s;
  }

  public synchronized DataFlavor[] getTransferDataFlavors() {
    return flavors;
  }
  public boolean isDataFlavorSupported( DataFlavor flavor ) {
    return (flavorList.contains(flavor));
  }

   public synchronized Object getTransferData(DataFlavor flavor)
    throws UnsupportedFlavorException, IOException {

    if (flavor.equals(StringTransferable.plainTextFlavor)) {
      String charset = flavor.getParameter("charset").trim();
      if(charset.equalsIgnoreCase("unicode")) {
    System.out.println("returning unicode charset");
    // uppercase U in Unicode here!
    return new ByteArrayInputStream(this.string.getBytes("Unicode"));
      } else {
    System.out.println("returning latin-1 charset");
    return new ByteArrayInputStream(this.string.getBytes("iso8859-1"));
      }
    } else if (StringTransferable.localStringFlavor.equals(flavor)) {
      return this.string;
    } else {
      throw new UnsupportedFlavorException (flavor);
    }
  }

    public void lostOwnership(Clipboard clpbrd, Transferable t) {

    }
}




class ColorTable extends JTable implements DropTargetListener,DragSourceListener, DragGestureListener
{


    private int mRow; // remember the row where the drag Started
    private int mColumn; // remember the Column where the drag Started
    private Object mDataToSet;
    private DragSource source;
    private JPopupMenu menu;
    private Selection selection;
    private boolean merge;
    private int dragRow = -1;
    private int dragCol = -1;
    private int startDragRow = -1;
    private int startDragCol = -1;

    public void setSelectionData(Selection s)
    {
        selection = s;
    }

    public void setCurrentDragCell(int row, int col)
    {
        dragRow = row;
        dragCol = col;
    }

    public void setStartDragCell(int row, int col)
    {
        startDragRow = row;
        startDragCol = col;
    }

    public ColorTable()
    {
        super();
        source = new DragSource();
        source.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_MOVE, this);
        source.addDragSourceListener(this);
        menu = new JPopupMenu();
//        JMenuItem item1 = new JMenuItem("item1");
//        item1.addActionListener(new ActionListener() {
//            public void actionPerformed(ActionEvent e) {
//            System.out.println("Menu item1 Test2");
//            }
//           });
//        JMenuItem item2 = new JMenuItem("item2");
//        item2.addActionListener(new ActionListener() {
//            public void actionPerformed(ActionEvent e) {
//            System.out.println("Menu item2 Test2");
//            }
//           });
//        JMenuItem item3 = new JMenuItem("item3");
//        item3.addActionListener(new ActionListener() {
//            public void actionPerformed(ActionEvent e) {
//            System.out.println("Menu item3 Test2");
//            }
//           });
//        JMenuItem item4 = new JMenuItem("item4");
//        item4.addActionListener(new ActionListener() {
//            public void actionPerformed(ActionEvent e) {
//            System.out.println("Menu item4 Test2");
//            }
//           });
//           menu.add(item1);
//           menu.add(item2);
//           menu.add(item3);
//           menu.add(item4);

        
           //addMouseListener(new PopupTriggerListener());

    }
    public JPopupMenu getPopupMenu()
    {
        return menu;
    }
    public void setMergeEnabled(boolean state)
    {
        merge = state;
    }

    private class PopupTriggerListener extends MouseAdapter {
      public void mousePressed(MouseEvent ev) {
        if (ev.isPopupTrigger()) {
          menu.show(ev.getComponent(), ev.getX(), ev.getY());
        }
      }

      public void mouseReleased(MouseEvent ev) {
        if (ev.isPopupTrigger()) {
          menu.show(ev.getComponent(), ev.getX(), ev.getY());
        }
      }

      public void mouseClicked(MouseEvent ev) {
      }
    }
    public void fillPopupMenu(Iterator<ColumnPare> iter)
    {
        //menu = new JPopupMenu();
        //while(iter.hasNext())
        //{
          //  menu.add(new  JCheckBoxMenuItem(iter.next().getDbcol().getName()));
        //}
    }
    public void dragDropEnd(DragSourceDropEvent dsde) {
// TODO Auto-generated method stub
    }

    public void dragGestureRecognized(DragGestureEvent dge) {
        // TODO Auto-generated method stub
        try{
        StringTransferable transferData;

            Point pt = dge.getDragOrigin();
            mRow = rowAtPoint(pt);
            mColumn = columnAtPoint(pt);
            setStartDragCell(mRow,mColumn);
            Object obj = getValueAt(mRow,mColumn);
            transferData = new StringTransferable(obj.toString());



        System.out.println("drag gesture recognized");
        source.startDrag(dge,DragSource.DefaultMoveDrop,transferData, this);
        }
        catch (Exception e)
        {
               System.out.println("Gesture exception");
        }
    }

    public void drop(DropTargetDropEvent dtde) {
        // TODO Auto-generated method stub
        System.out.println("drop method start");
        try {
        dtde.acceptDrop(DnDConstants.ACTION_MOVE);
        System.out.println("drop accept");
        Point p = dtde.getLocation();
        int row = rowAtPoint(p);
        int column = columnAtPoint(p);
        mDataToSet = getValueAt(row,column);
        Transferable transferData = dtde.getTransferable();
        Object data = transferData.getTransferData(DataFlavor.stringFlavor);
        if(!merge)
        {
            ((DocTableModel)getModel()).setValueAt(data,row, column);
            ((DocTableModel)getModel()).setValueAt(mDataToSet,mRow,mColumn);
        }
        else
        {
            Object obj = ((DocTableModel)getModel()).getValueAt(row, column);
            String s = new String();
            s = obj.toString() + data.toString();
            ((DocTableModel)getModel()).setValueAt(s,row, column);
        }

        dtde.dropComplete(true);
        setStartDragCell(-1,-1);
        setCurrentDragCell(-1,-1);
        System.out.println("drop end");
        } catch (Exception e) {
        }
    }

    public void dragEnter(DropTargetDragEvent dtde) {


        dtde.acceptDrag(DnDConstants.ACTION_MOVE);

    }

    public void dragOver(DropTargetDragEvent dtde) {
        Point p = dtde.getLocation();
        int row = rowAtPoint(p);
        int column = columnAtPoint(p);
        setCurrentDragCell(row,column);
        repaint();
        dtde.acceptDrag(DnDConstants.ACTION_MOVE);
    }

    public void dropActionChanged(DropTargetDragEvent dtde) {
        dtde.acceptDrag(DnDConstants.ACTION_MOVE);
    }

    public void dragExit(DropTargetEvent dte) {

    }

    public void dragEnter(DragSourceDragEvent dsde) {
        DragSourceContext context = dsde.getDragSourceContext();

      int myaction = dsde.getDropAction();
      if( (myaction & DnDConstants.ACTION_MOVE) != 0) {
    context.setCursor(DragSource.DefaultMoveDrop);
  
      } else {
    context.setCursor(DragSource.DefaultMoveNoDrop);
      }

    }

    public void dragOver(DragSourceDragEvent dsde) {

    }

    public void dropActionChanged(DragSourceDragEvent dsde) {

    }

    public void dragExit(DragSourceEvent dse) {

    }

            @Override
            public void tableChanged(TableModelEvent e) {
                super.tableChanged(e);

                repaint();
            }

            @Override
            public Component prepareRenderer(TableCellRenderer renderer,
                    int row, int col) {
                Component c = super.prepareRenderer(renderer, row,    col);
                if ((row == startDragRow) && (col == startDragCol))
                {
                    c.setBackground(Color.gray);
                }
                else
                    c.setBackground(Color.white);
                if (isCellSelected(row,col))
                    c.setBackground(Color.blue);
                if ((row == dragRow)&&(col == dragCol))
                {
                    c.setBackground(Color.gray);
                }
                 /*
                else
                {
                    if (selection != null)
                    {
                        if ((row>=selection.getY()) && (row<= (selection.getY() + selection.getHight() ))
                            && (col>=selection.getX()) && (col<= (selection.getX()+selection.getWidth())))
                                c.setBackground(Color.green);
                        else
                            c.setBackground(getBackground());
                    }
                    else {
                        c.setBackground(Color.white);
                    }
                }*/
                return c;
            }

//            @Override
//            public void changeSelection(int rowIndex, int columnIndex,
//                    boolean toggle, boolean extend) {
//                    super.changeSelection(rowIndex, columnIndex, true, false);
//            }
}


public class DocTable extends JPanel implements IObserver,CathegoryListener, DocTextListener{

    protected ColorTable table;
    protected JScrollPane scroller;
    protected DocTableModel tableModel;
    //protected ColoredTableCellRenderer tableRenderer;
    private Vector<IObserver> observers = new Vector();
    private Sender sender;
    protected JToolBar toolbar;
    protected JCheckBox checkbox;
    protected DocTextListener listener = null;
    private int col = -1;
    private int row = -1;

    public void addEvent(String name) {
        if (sender == Sender.RESULT)
        {
            int[] i = table.getSelectedRows();
            if(i.length!=0)
            {
                for (int x = i[0];x<i.length; ++x)
                {
                    tableModel.setValueAt(name, x, table.getColumnCount()-1);
                }
            }
            repaint();
        }
    }
    public ColorTable getColorTable()
    {
        return table;
    }
    public void setListener(DocTextListener _listener) {
        listener = _listener;
    }

    public void notifyTextChange(String text) {
       col = table.getSelectedColumn();
       row = table.getSelectedRow();
       tableModel.setValueAt(text, row, col);
    }

    private class SelectionListener implements ListSelectionListener {
            JTable jtable;
            int column;
            int rowt;

            // It is necessary to keep the table since it is not possible
            // to determine the table from the event's source
            SelectionListener(JTable table) {
                this.jtable = table;
            }
            public void valueChanged(ListSelectionEvent e) {
                // If cell selection is enabled, both row and column change events are fired
                if (e.getSource() == jtable.getSelectionModel()) 
                {
                    
                    rowt = jtable.getSelectedRow();
                    if (column>=0)
                    {
                        Object obj = tableModel.getValueAt(rowt, column);
                        listener.notifyTextChange(obj.toString());
                    }
                }

                if ((e.getSource() == jtable.getColumnModel().getSelectionModel() ) )
                {

                    column = jtable.getSelectedColumn();
                    rowt = jtable.getSelectedRow();
                    if ((rowt>=0) )
                    {
                        Object obj = tableModel.getValueAt(rowt, column);
                        listener.notifyTextChange(obj.toString());
                    }
                }

                if (e.getValueIsAdjusting()) {
                    // The mouse button has not yet been released
                }
            }
        }

    private class DocTableListener implements TableModelListener
    {

        public void tableChanged(TableModelEvent tme) {
            if (listener != null)
            {
                col = tme.getColumn();
                row = tme.getFirstRow();
                TableModel model = (TableModel) tme.getSource();
                if(table.isCellSelected(row, col))
                {
                    Object obj = model.getValueAt(row, col);
                    listener.notifyTextChange(obj.toString());
                }
            }
        }
        
    };

//    private class MyTransferHandler extends TransferHandler
//{
//
//      public boolean canImport(TransferSupport support) {
//        // for the demo, we'll only support drops (not clipboard paste)
//          System.out.println("can import method call");
//        /*if (!support.isDrop()) {
//          return false;
//        }
//
//        // we only import Strings
//        if (!support.isDataFlavorSupported(DataFlavor.stringFlavor)) {
//          return false;
//        }*/
//        System.out.println("can import - true");
//        return true;
//      }
//
//
//      public boolean importData(TransferSupport support) {
//        // if we can't handle the import, say so
//        if (!canImport(support)) {
//          return false;
//        }
//
//        // fetch the drop location
//        JTable.DropLocation dl = (JTable.DropLocation) support
//            .getDropLocation();
//
//        int row = dl.getRow();
//        int col = dl.getColumn();
//
//
//        // fetch the data and bail if this fails
//        String data;
//        try {
//          data = (String) support.getTransferable().getTransferData(
//              DataFlavor.stringFlavor);
//        } catch (UnsupportedFlavorException e) {
//          return false;
//        } catch (IOException e) {
//          return false;
//        }
//
//
//        tableModel.setValueAt(data, row, col);
//
//
//
//        // demo stuff - remove for blog
//
//        // end demo stuff
//
//        return true;
//      }};

    private class KeyHandler implements KeyListener
    {

        public void keyTyped(KeyEvent ke) {
           // throw new UnsupportedOperationException("Not supported yet.");
        }

        public void keyPressed(KeyEvent ke) {
            int code = ke.getKeyCode();
            if ( code == ke.VK_ENTER)
            {
                if (ke.isShiftDown())
                    addColumnOnKeyStroke();
                else
                    addRowOnKeyStroke();
            }
            else if (code == ke.VK_DELETE)
            {
                if (ke.isShiftDown())
                    removeColumnOnKeyStroke();
                else
                    removeRowOnKeyStroke();
            }

        }

        public void keyReleased(KeyEvent ke) {
            //throw new UnsupportedOperationException("Not supported yet.");
        }

    };
    //protected DefaultTableCellRenderer tableRenderer;

     public DocTable() {
         initComponent();
     }

    public DocTable(IObserver.Sender _sender)
    {
        sender = _sender;
         initComponent();
    }

     private void initComponent() {
         tableModel = new DocTableModel();
         //tableRenderer = new ColoredTableCellRenderer();
         //tableRenderer = new DefaultTableCellRenderer();
         //tableModel.addTableModelListener(new InteractiveForm.InteractiveTableModelListener());
         table = new ColorTable();
        
         table.setModel(tableModel);
         //table.setDefaultRenderer(String.class, tableRenderer);
         table.setColumnSelectionAllowed(true);
         table.setRowSelectionAllowed(true);
         table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
         //table.setSelectionBackground(Color.blue);
         //table.setCellSelectionEnabled(true);
         KeyHandler listener = new KeyHandler();
         table.addKeyListener(listener);
         table.setDragEnabled(true);
         table.setDropTarget(new DropTarget(table,table));
//         table.setTransferHandler(new MyTransferHandler() );
         tableModel.addTableModelListener(new DocTableListener());
         SelectionListener list = new SelectionListener(table);
         table.getSelectionModel().addListSelectionListener(list);
         table.getColumnModel().getSelectionModel().addListSelectionListener(list);
         scroller = new javax.swing.JScrollPane(table);
         //table.setPreferredScrollableViewportSize(new java.awt.Dimension(500, 300));
         /*TableColumn hidden = table.getColumnModel().getColumn(InteractiveTableModel.HIDDEN_INDEX);
         hidden.setMinWidth(2);
         hidden.setPreferredWidth(2);
         hidden.setMaxWidth(2);
         hidden.setCellRenderer(new InteractiveRenderer(InteractiveTableModel.HIDDEN_INDEX));*/
         toolbar = new JToolBar();
         JLabel label = new JLabel("Объединять");
         checkbox = new JCheckBox();
         checkbox.addActionListener(new ActionListener(){

            public void actionPerformed(ActionEvent ae) {
                if(ae.getSource() == checkbox)
                {
                    if (checkbox.isSelected())
                        table.setMergeEnabled(true);
                    else
                        table.setMergeEnabled(false);
                }
            }
         });
         toolbar.add(label);
         toolbar.add(checkbox);
         setLayout(new BorderLayout());
         add(scroller, BorderLayout.CENTER);
         add(toolbar,BorderLayout.NORTH);
     }

     public void addColumn(String text)
     {
         tableModel.addColumn(text);
         tableModel.fireTableStructureChanged();
     }

     public void addNewDataRow(Vector<String> values)
     {
         tableModel.addRow(values);
         //tableModel.fireTableStructureChanged();
     }
     public void createTable(Vector<String> headers)
     {
         Iterator<String> it = headers.iterator();
         while(it.hasNext())
         {
             addColumn(it.next());
         }
     }

    public void fillDeprecated(Iterator<Table> it)
    {
        Table t_table;
        if(it.hasNext())
        {
            t_table = it.next();
            Vector<String> headers = new Vector();
            for (int col = 0; col<t_table.getCols(0);col++)
                headers.add(t_table.getData(0, col));
           createTable(headers);
        }
        while(it.hasNext())
        {
            t_table = it.next();
            for (int row = 1; row<t_table.numRows();row++)
            {
                Vector<String> value = new Vector();
                for (int col = 0; col<t_table.getCols(row); col++ )
                    value.add(t_table.getData(row, col));

                addNewDataRow(value);
            }
        }
    }

    public void fill(Iterator<Table> it)
    {
        int maxCols = 0;
        int numRows = 0;
        if (it.hasNext())
        {
            Table t = it.next();
            numRows = t.numRows();
            for (int i =0; i<numRows; i++)
                if (t.getCols(i) > maxCols)
                    maxCols = t.getCols(i);
            for(int i =0;i < maxCols; i++)
                addColumn(String.valueOf(i+1));
            for(int i = 0; i<numRows; i++)
            {
                Vector<String> value = new Vector();
                int j;
                for(j = 0; j <t.getCols(i);j++)
                    value.add(t.getData(i, j));
                if (j<maxCols)
                    for (;j<maxCols;j++)
                        value.add(" ");
                addNewDataRow(value);
            }
        }

    }

    private void addRowOnKeyStroke()
    {
        if (table.getSelectedColumn() == table.getModel().getColumnCount()-1
                && table.getSelectedRow() == table.getModel().getRowCount()-1)
        {
            addNewDataRow(new Vector( tableModel.getColumnCount() ) );
        }
    }

    private void removeRowOnKeyStroke()
    {
        if (table.getSelectedRowCount() == 1)
        {
                tableModel.removeRow( table.getSelectedRow() );
        }
        else
        {
            int[] mas = table.getSelectedRows();
            for (int i = mas.length -1; i >=0 ; i--)
                tableModel.removeRow( mas[i] );
        }
        tableModel.fireTableStructureChanged();
    }

    private void addColumnOnKeyStroke()
    {
       if (table.getSelectedColumn() == tableModel.getColumnCount()-1)
       {
           addColumn(String.valueOf(tableModel.getColumnCount()));
       }
    }
    public int getNumCols()
    {
        
        return table.getColumnModel().getColumnCount();
    }
    public void clearTable()
    {    
        tableModel.setColumnCount(0);
        tableModel.setRowCount(1);
    }
    public void fillPopupMenu(Iterator<ColumnPare> iter)
    {
        if (table!=null)
        {
            table.fillPopupMenu(iter);
        }
    }
    public void removeColumn(int i)
    {
        TableColumn tcolumn = table.getColumnModel().getColumn(i);
            int columnModelIndex = tcolumn.getModelIndex();
            Vector data = tableModel.getDataVector();
            Vector colIds = tableModel.getColumnIdentifiers();

            // Remove the column from the table
            table.removeColumn(tcolumn) ;

            // Remove the column header from the table model
            colIds.removeElementAt(columnModelIndex);

    // Remove the column data
            for (int r=0; r<data.size(); r++) {
                Vector row = (Vector)data.get(r);
                row.removeElementAt(columnModelIndex);
            }
            tableModel.setDataVector(data, colIds);

            // Correct the model indices in the TableColumn objects
            // by decrementing those indices that follow the deleted column
            Enumeration en = table.getColumnModel().getColumns();
            for (; en.hasMoreElements(); ) {
                TableColumn c = (TableColumn)en.nextElement();
                if (c.getModelIndex() >= columnModelIndex) {
                    c.setModelIndex(c.getModelIndex()-1);
                }
            }
            tableModel.fireTableStructureChanged();
    }

    private void removeColumnOnKeyStroke()
    {
        if (table.getSelectedColumnCount() == 1)
        {
            removeColumn(table.getSelectedColumn());
        }
        else
        {
            int[] mas = table.getSelectedColumns();
            for (int i = mas.length -1; i >=0 ; i--)
                removeColumn( mas[i] );
        }
    }

 

     public void add(int i) {
        int[] cols = table.getSelectedColumns();
        int[] rows = table.getSelectedRows();
        if ((cols.length!= 0) && (rows.length!=0))
        {
            Selection selection = new Selection(cols[0],rows[0],
                cols[cols.length-1],rows[rows.length-1]);
            createSelection(selection,sender,i);
            table.clearSelection();
        }
    }

    public void highlightOn(int i) {
       
    }

    public void highlightOff() {
        table.setSelectionData(null);
        repaint();
    }

    public void highlight(Selection one, Sender _sender) {
        if (_sender == sender)
        {
            table.setSelectionData(one);
            repaint();
        }
    }

    public void delete(int i) {
        
    }

    public void createSelection(Selection selection, Sender sender,int i) {
        Iterator<IObserver> it = observers.iterator();
        while(it.hasNext())
        {
            it.next().createSelection(selection, sender, i);
        }
    }

    public void addListener(IObserver obj) {
        observers.add(obj);
    }

    public void deleteListener(IObserver obj) {
        Iterator<IObserver> it = observers.iterator();
        while(it.hasNext())
        {
            if(it.next().equals(obj))
            {
                it.remove();
                break;
            }

        }
    }

}
